package com.zjw.socket.demo.starter.plugin.impl.database;

import com.zjw.socket.demo.starter.annotation.NonNull;
import com.zjw.socket.demo.starter.smi.IFutureTask;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.sqlclient.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 执行器
 */
public final class SqlExecutor {

  private final Map<String, PreparedQuery<RowSet<Row>>> mSqlCache = new HashMap<>();

  private PreparedQuery<RowSet<Row>> requireSql(@NonNull SqlClient client, @NonNull String sql) {
    return mSqlCache.compute(sql, (s, rowSetPreparedQuery) -> client.preparedQuery(sql));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute().onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute(Tuple.of(elt1)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1, Object elt2) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute(Tuple.of(elt1, elt2)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1, Object elt2, Object elt3) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute(Tuple.of(elt1, elt2, elt3)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1, Object elt2, Object elt3, Object elt4) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute(Tuple.of(elt1, elt2, elt3, elt4)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1, Object elt2, Object elt3, Object elt4, Object elt5) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);

    preparedQuery.execute(Tuple.of(elt1, elt2, elt3, elt4, elt5)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public <T> void doSqlExecute(@NonNull SqlClient client, @NonNull IRowConvert<T> convert, @NonNull IFutureTask<List<T>> call, @NonNull String sql, Object elt1, Object elt2, Object elt3, Object elt4, Object elt5, Object elt6) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);
    preparedQuery.execute(Tuple.of(elt1, elt2, elt3, elt4, elt5, elt6)).onComplete(new SqlListHandler<T>(convert, call));
  }

  public void doRealSqlExecute(SqlClient client, IRowConvert<?> convert, IFutureTask<List<?>> call, String sql) {
    PreparedQuery<RowSet<Row>> preparedQuery = requireSql(client, sql);
    preparedQuery.execute().onComplete(new SqlListHandler(convert, call));
  }

  /**
   * RowSet<Row>专用Handler，通过此Handler可简化一些非必要代码
   */
  private static class SqlListHandler<T> implements Handler<AsyncResult<RowSet<Row>>> {

    IRowConvert<T> convert;

    IFutureTask<List<T>> call;

    SqlListHandler(IRowConvert<T> convert, IFutureTask<List<T>> call) {
      this.convert = convert;
      this.call = call;
    }

    @Override
    public void handle(AsyncResult<RowSet<Row>> event) {
      if (event.succeeded()) {
        RowSet<Row> result = event.result();
        ArrayList<T> list = new ArrayList<>();
        for (Row row : result) {
          list.add(convert.convert(row));
        }
        call.onResult(list);
      } else {
        call.onError(event.cause());
      }
    }
  }

}
